#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "include/params.h"
#include "include/types.h"
#include "include/operat.h"
#include "include/module.h"
#include "include/virtware.h"
#include "include/vmerr.h"
#include "include/libcore.h"

status env_init (struct environ * env, uint32 memsz);
static status env_ctl_start (struct environ * env);
static status env_ctl_stop (struct environ * env);
static status env_ctl_reset (struct environ * env);
static status env_ctl_pause (struct environ * env);
static status env_ctl_resume (struct environ * env);
status env_ctrl (struct environ * env, enum env_ctl_arg arg) ;
status env_add_module (struct environ * env, struct module * mod);
status env_remove_module (struct environ * env, modid_t modid);
status env_csrrw_sync (struct environ * env, csraddr_t addr, word send, word * recv, enum rwmod rw);
status env_csrrw_async (struct environ * env, csraddr_t addr, word send, word * recv, enum rwmod rw, struct req_stat * async_stat);
status env_mod_ctrl (struct environ * env, modid_t modid, enum mod_ctl_arg arg, uint64 exarg);

status
env_init (struct environ * env, uint32 memsz) {
    status stat = STAT_SUCCESS;
    if (MAGIC_OK (env)) {
        stat |= ERR_AGAIN;
        DBG_LOG (env_init, "env already initialized");
        goto error;
    }
    bzero (env, sizeof (struct environ));
    stat |= cpu_init (&env->cpu, env);
    stat |= mem_init (&env->mem, memsz ,env);
    stat |= devctl_init (&env->dctl, env);
    mlk_init (env->ctrl_mlk);
    mlk_init (env->mod_ctrl_mlk);
    if (stat == STAT_SUCCESS) {
        env->magic = BISCUIT_MAGIC;
    }
    return stat;
    error:
    return stat;
}


static status
env_ctl_start (struct environ * env) {
    status stat = STAT_SUCCESS;
    if (!MAGIC_OK (env)) {
        stat |= ERR_MAGIC;
        DBG_LOG (env_ctl_start, "Magic error");
        goto error;
    }
    if (env->stat != ENV_STOPPED) {
        stat |= ERR_STATUS;
        DBG_LOG (env_ctl_start, "Stat error (not STOPPED)");
        goto error;
    }
    count_t i;
    status mstat;
    for (i = 0; i < env->mod_n; i++) {
        mstat = env->mods[i]->start (env, env->mods[i]);
        if (mstat != STAT_SUCCESS) {
            DBG_LOG (env_ctl_start, "Unable to start mod[%llu]", env->mods[i]->modid);
        }
    }
    stat |= cpu_ctrl (&env->cpu, CPU_CTL_START);
    if (stat != STAT_SUCCESS) {
        DBG_LOG (env_ctl_start, "Unable to start cpu");
        goto error;
    }
    stat |= devctl_ctrl (&env->dctl, DEV_CTL_START);
    if (stat != STAT_SUCCESS) {
        DBG_LOG (env_ctl_start, "Unable to start devctl");
        goto error;
    }
    env->stat = ENV_RUNNING;
    return stat;
    error:
    return stat;
}

static status
env_ctl_stop (struct environ * env) {
    status stat = STAT_SUCCESS;
    if (!MAGIC_OK (env)) {
        stat |= ERR_MAGIC;
        DBG_LOG (env_ctl_stop, "Magic error");
        goto error;
    }
    if (env->stat != ENV_RUNNING) {
        stat |= ERR_STATUS;
        DBG_LOG (env_ctl_stop, "Stat error (not RUNNING)");
        goto error;
    }
    count_t i;
    status mstat;
    for (i = 0; i < env->mod_n; i++) {
        mstat = env->mods[i]->stop (env, env->mods[i]);
        if (mstat != STAT_SUCCESS) {
            DBG_LOG (env_ctl_stop, "Unable to stop mod[%llu]", env->mods[i]->modid);
        }
        printf ("Module[%llu:\"%s\"] stopped.\n", env->mods[i]->modid, env->mods[i]->name);
    }
    stat |= cpu_ctrl (&env->cpu, CPU_CTL_STOP);
    stat |= devctl_ctrl (&env->dctl, DEV_CTL_STOP);
    if (stat) {
        DBG_LOG (env_ctl_stop, "Unable to stop cpu or devctl");
        goto error;
    }
    //printf ("ENV stopped");
    env->stat = ENV_STOPPED;
    return stat;
    error:
    return stat;
}

static status
env_ctl_reset (struct environ * env) {
    (void) env;
    return ERR_NOIMPL;
}

static status
env_ctl_pause (struct environ * env) {
    (void) env;
    return ERR_NOIMPL;
}

static status
env_ctl_resume (struct environ * env) {
    (void) env;
    return ERR_NOIMPL;
}


status
env_ctrl (struct environ * env, enum env_ctl_arg arg) {
    status stat = STAT_SUCCESS;
    mlk_lock (env->ctrl_mlk);
    if (MAGIC_OK(env) == false) {
        DBG_LOG (env_ctrl, "Magic error.");
        stat |= ERR_MAGIC;
        goto error;
    }
    switch (arg)
    {
    case ENV_CTL_START: {
        stat |= env_ctl_start (env);
        break;
    }
    case ENV_CTL_STOP: {
        stat |= env_ctl_stop (env);
        break;
    }
    case ENV_CTL_RESET: {
        stat |= env_ctl_reset (env);
        break;
    }
    case ENV_CTL_PAUSE: {
        stat |= env_ctl_pause (env);
        break;
    }
    case ENV_CTL_RESUME: {
        stat |= env_ctl_resume (env);
        break;
    }
    default: {
        //printf ("DEFAULT");
        stat |= ERR_NOIMPL;
        break;
    }
    }
    mlk_unlock (env->ctrl_mlk);
    return stat;
    error:
    mlk_unlock (env->ctrl_mlk);
    return stat;
}

/* 添加并初始化mod */
status
env_add_module (struct environ * env, struct module * mod) {
    status stat = STAT_SUCCESS;
    if (env->mod_n >= MAX_MOD_ITEM) {
        stat |= ERR_NRMOD;
        DBG_LOG (env_add_module, "Unable to add more mods.");
        goto error;
    }
    env->mods[env->mod_n] = mod;
    stat |= env->mods[env->mod_n]->init(env, mod); // mod在此时应该已经创建ctx
    stat |= cpu_add_module (&env->cpu, mod);
    stat |= mem_add_module (&env->mem, mod);
    stat |= devctl_add_module (&env->dctl, mod);
    if (stat == STAT_SUCCESS) {
        env->mod_n++;
        printf ("Module[%llu:\"%s\"] installed.\n", mod->modid, mod->name);
        DBG_LOG (env_add_module, "Mod[%llu:\"%s\"] successfully installed.", mod->modid, mod->name);
    } else {
        DBG_LOG (env_add_module, "Unable to init mod.");
        stat |= ERR_MODFN;
        goto error;
    }
    return stat;
    error:
    return stat;
}

/* 终止并移除mod */
status
env_remove_module (struct environ * env, modid_t modid) {
    status stat = STAT_SUCCESS;
    count_t i;
    struct module * rmmod;
    status mstat = STAT_SUCCESS;
    for (i = 0; i < env->mod_n; i++) {
        if (env->mods[i]->modid == modid) {
            rmmod = env->mods[i];
            mstat = env->mods[i]->stop(env, rmmod);
            if (mstat == STAT_SUCCESS) {
                // 如果模块存在就一定会移除模块
                cpu_remove_module (&env->cpu, modid);
                mem_remove_module (&env->mem, modid);
                devctl_remove_module (&env->dctl, modid);
                count_t j;
                for (j = i; j < env->mod_n-1; j++)
                    env->mods[j] = env->mods[j+1];
                env->mod_n--;
                goto success;
            } else {
                DBG_LOG (env_remove_module, "unable to stop mod");
                stat |= ERR_MODFN;
                goto error;
            }
        }
    }
    DBG_LOG (env_remove_module, "mod not found.");
    error:
    return stat;
    success:
    return stat;
}


status
env_csrrw_sync (struct environ * env, csraddr_t addr, word send, word * recv, enum rwmod rw) {
    status stat = STAT_SUCCESS;
    struct req_stat rstat = REQS_INIT;
    struct rv32cpu * cpu = &env->cpu;
    struct devctl * dctl = &env->dctl;
    /* 同步部分 */
    exec_module_func (cpu, csrrw, {
        .addr = addr,
        .env = cpu->env,
        .send = send,
        .recv = recv,
        .rw = rw,
        .rstat = &rstat
    });
    stat |= devctl_csrrw_sync (dctl, addr, send, recv, rw);
    return stat;
}

status
env_csrrw_async (struct environ * env, csraddr_t addr, word send, word * recv, enum rwmod rw, struct req_stat * async_stat) {
    status stat = STAT_SUCCESS;
    // struct req_stat rstat = REQS_INIT;
    struct rv32cpu * cpu = &env->cpu;
    struct devctl * dctl = &env->dctl;
    /* 同步部分 */
    exec_module_func (cpu, csrrw, {
        .addr = addr,
        .env = cpu->env,
        .send = send,
        .recv = recv,
        .rw = rw,
        .rstat = async_stat
    });
    if (IS_RMODE (rw)) {
        // 读模式要求同步读取
        stat |= devctl_csrrw_sync (dctl, addr, send, recv, rw);
    } else {
        // 写模式可异步执行
    /* 异步部分 启动devctl */
        if (dctl->stat.running) {
            // bzero (async_stat, sizeof (struct req_stat));
            async_stat->req_fin = false;
            int op_suc = 0;
            struct dev_csrrw_req csrreq_buf = {
                .addr = addr,
                .env = cpu->env,
                .send = send,
                .recv = recv,
                .rw = rw,
                .rstat = async_stat
            };
            plk_lock (dctl->csrrw_ring.plk, PLK_DEVCTL);
            op_suc = ring_pushtl (dctl->csrrw_ring, csrreq_buf);
            plk_unlock (dctl->csrrw_ring.plk, PLK_DEVCTL);
            if (!op_suc)
                stat |= ERR_FULLRING;
        }
    }
    return stat;
}

status
env_mod_ctrl (struct environ * env, modid_t modid, enum mod_ctl_arg arg, uint64 exarg) {
    status stat = STAT_SUCCESS;
    mlk_lock (env->mod_ctrl_mlk);
    struct module * currmod;
    count_t i;
    for (i = 0; i < env->mod_n; i++) {
        if (env->mods[i]->modid == modid) {
            currmod = env->mods[i];
            goto found;
        }
    }
    stat |= ERR_MNOINST;
    goto error;
    found:
    switch (arg)
    {
    case MOD_CTL_CTRL: {
        stat |= currmod->modctl (env, currmod, exarg);
        break;
    }
    case MOD_CTL_PAUSE : {
        stat |= currmod->pause (env, currmod);
        break;
    }
    case MOD_CTL_RESUME : {
        stat |= currmod->resume (env, currmod);
        break;
    }
    case MOD_CTL_START : {
        stat |= currmod->start (env, currmod);
        break;
    }
    case MOD_CTL_STOP : {
        stat |= currmod->stop (env, currmod);
        break;
    }
    default: {
        stat |= ERR_NOIMPL;
        break;
    }
    }
    if (stat != STAT_SUCCESS) {
        DBG_LOG (env_mod_ctrl, "mod ctrl failed");
        goto error;
    }
    mlk_unlock (env->mod_ctrl_mlk);
    return stat;
    error:
    mlk_unlock (env->mod_ctrl_mlk);
    return stat;
}
